﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using ESpace;
using System.IO;
using System.Media;
using Ord;

namespace Ordonnance
{
    public partial class FormMain : Form
    {

        #region *** модели **************************************
        EDX.ExMesh[] mCard;
        EDX.ExMesh mTown;
        EDX.ExMesh mTown1;
        EDX.ExMesh mSelTown;
        EDX.ExMesh[] mSelCard;
        EDX.ExMesh mTable;
        EDX.ExMesh mT1;
        EDX.ExMesh mT2;
        EDX.ExMesh mT3;
        EDX.ExMesh mFlag;
        Bitmap[] bmpCard = new Bitmap[32];
        ESprite mWin;
        EDX.ExMesh mSelPl;
        #endregion *** модели **************************************

        #region *** позиции **************************************

        Position[] PosCard;

        #endregion *** позиции **************************************

        #region *** переменные для игры ******************************

        Microsoft.DirectX.Direct3D.Device dvc; // устройство
        SoundPlayer[] snd = new SoundPlayer[10];
        Microsoft.DirectX.Direct3D.Font fnt; // основной игровой шрифт
        Microsoft.DirectX.Direct3D.Font fntWin; // шрифт для подведени итогов игры
        int countFrame; // счетчик кадров
        float elTime; // пройденое время с последнего кадра
        ECamera Cam; // камера
        float AngVis = 45; // текущий угол поля видимости камеры
        //ESprite crs; // игровой курсор

        bool CamIsMove = false; // камера движется
        bool[] cmd = new bool[9];  // команды
        float stepXPl = 35; // шаг между игроками по X
        float stepZCard = 1.2f;  // шаг между картами по Z
        int stepYCrd = 50; // шаг между картами в руке по Y
        int shYCrd = 10; // смещение по Y от верхнего края поля для карт в руке
        Vector2 mouseP = new Vector2(); // текущее положение курсора
        System.Drawing.Point PanelC; // позиция центра панели в координатах дисплея (для движения камеры по движению мыши)
        bool bInPanel = true; // курсор находится на территории панели

        List<String> ul = new List<String>(); // юзер-лист, залогиненых на сервер
        List<Game> gmL = new List<Game>(); // список доступных игр

        // лист для замены смайлов
        List<String[]> SmL = new List<String[]>();

        bool bShowInfo = false; // показывать информацию по игре

        #region *** текущие переменные игры для сокращения имен ***********

        int SelTown { get { return gmL[actG].SelTown; } set { gmL[actG].SelTown = value; } } // выбранный игроком город для добавления карты
        int Ipl { get { return gmL[actG].Ipl; } set { gmL[actG].Ipl = value; } } // игрок, которым играет клиент
        Byte playPl { get { return gmL[actG].PlayPl; } set { gmL[actG].PlayPl = value; } } // игрок, ходящий в данный момент
        List<Player> pl; // список игроков
        //public List<Card> pack; // колода карт (существует только на сервере)
        public bool bPanaram { get { return gmL[actG].bPanaram; } set { gmL[actG].bPanaram = value; } }  // панарамный вид

        int pActG = -1;
        /// <summary>запущенная игра, в которую играет игрок</summary>
        int actG
        {
            get { return pActG; }
            set
            {
                pActG = value;
                if (pActG != -1)
                {
                    pl = gmL[pActG].pl; // список игроков
                }
            }
        }

        #endregion *** текущие переменные игры для сокращения имен ***********

        // выбранная мышкой карта/город/игрок
        class SelectedCard
        {
            public int p;
            public int t;
            public int c;
            public SelectedCard(int P, int T, int C)
            {
                p = P;
                t = T;
                c = C;
            }
            public void Set(int P, int T, int C)
            {
                p = P;
                t = T;
                c = C;
            }
        }
        SelectedCard sCard = new SelectedCard(-1, -1, -1);

        // для выбранного игрока
        float cntSelPl = 0;
        bool bSelPlDown = false;
        Color clrSelPl;

        #endregion *** переменные для игры ******************************

        #region *** для перемещения камеры *******************************
        Position speed = new Position(0, 0, 0, 0, 0, 45);
        Position destP = new Position(0, 0, 0, 0, 0, 45);
        float minDiff = float.MaxValue;
        #endregion *** для перемещения камеры *******************************

        #region *** для предигрового меню, коммуникаций и пр *************************

        FormConn frm; // форма коммуникаций 
        bool bGame = false; // режим игры

        // ожидание отправки следующей команды
        bool bWait
        {
            get { return !frm.serverIsAvailable || idQuery != 0; }
        }

        // id текущего запроса на сервер (если нет ожидания ответа на запрос, то 0)
        int pIdQuery = 0;
        int idQuery
        {
            get { return pIdQuery; }
            set
            {
                pIdQuery = value;
                bool en = pIdQuery == 0;
                tBtnUpdateTable.Enabled = en;
                btnSendChat.Enabled = en;
                btnUpdateGameList.Enabled = en;
                tBtnChangePass.Enabled = en;
                btnCreateGame.Enabled = en;

            }
        }

        FormSmiles frmSm = new FormSmiles();

        Game SelGame { get { if (lvGames.SelectedIndices.Count > 0) return (Game)lvGames.SelectedItems[0].Tag; return null; } }
        Player SelPlr { get { if (lvPlayers.SelectedIndices.Count > 0) return (Player)lvPlayers.SelectedItems[0].Tag; return null; } }

        #endregion *** для предигрового меню, коммуникаций и пр *************************

        public FormMain(FormConn Frm)
        {
            InitializeComponent();
            try
            {
                snd[0] = new SoundPlayer(Application.StartupPath + "\\Data\\Sounds\\MessInChat.wav");
                snd[1] = new SoundPlayer(Application.StartupPath + "\\Data\\Sounds\\MessSent.wav");
                snd[2] = new SoundPlayer(Application.StartupPath + "\\Data\\Sounds\\YourPlay.wav");
                snd[3] = new SoundPlayer(Application.StartupPath + "\\Data\\Sounds\\Stake.wav");
            }
            catch (Exception ex)
            {
                MessageBox.Show("Не удалось загрузить звуки\r\n" + ex.Message);
            }

            // директория истории
            if (!Directory.Exists("History")) Directory.CreateDirectory("History");

            // подсказки к кнопкам
            toolTip1.SetToolTip(btnClearChat, "Очистить окно чата");
            toolTip1.SetToolTip(btnChatHist, "Показать историю чата");

            frm = Frm;

            MouseWheel += new MouseEventHandler(Frm_MouseWheel);
            PanelC = new System.Drawing.Point(panel1.Width / 2, panel1.Height / 2);

            //BuildCardTx();
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
            PosCard = new Position[8];
            for (int i = 0; i < 8; i++)
            {
                float aX = 70;
                float aY = 0;
                float z = -14.8f;
                if (i % 2 == 1)
                {
                    aY = 180;
                    z = 14.8f;
                }
                PosCard[i] = new Position(stepXPl * (i / 2), 34, z, aX, aY, AngVis);
            }

            // создаем рисунки карт для отрисовки карт в руке
            for (int i = 1; i < 32; i++)
            {
                bmpCard[i] = new Bitmap(Application.StartupPath + "\\Data\\Models\\CardsTx\\" + i.ToString("00") + ".png");
            }

            DrawChatList();
        }

        #region *** инициализация графики *********************************************

        public void InitializeGraphics()
        {
            try
            {
                // создание устройства
                dvc = EDX.CreareDevice(panel1, true, OnDeviceReset, false);
                dvc.DeviceResizing += new CancelEventHandler(OnResizeDvc);

                // загрузка моделей
                //crs = new ESprite(dvc, Application.StartupPath + "\\Data\\Models\\Crs.png");
                mCard = new EDX.ExMesh[32];
                DrawLoad(0);
                for (int i = 0; i < 32; i++)
                {
                    mCard[i] = EDX.LoadMesh(Application.StartupPath + "\\Data\\Models\\Card.X", Application.StartupPath + "\\Data\\Models", false);
                    DrawLoad(0.7f);
                }
                Color clrNone = Color.FromArgb(0, 255, 255, 255);
                //mCard.mt[0] = EDX.CreateMaterial(clrNone, clrNone, clrNone, clrNone,0);
                mTown = EDX.LoadMesh(Application.StartupPath + "\\Data\\Models\\Town.X", Application.StartupPath + "\\Data\\Models", false);
                mTown1 = EDX.LoadMesh(Application.StartupPath + "\\Data\\Models\\Town1.X", Application.StartupPath + "\\Data\\Models", false);
                DrawLoad(2);
                mSelTown = EDX.LoadMesh(Application.StartupPath + "\\Data\\Models\\2.X", Application.StartupPath + "\\Data\\Models", false);
                DrawLoad(2);
                mSelCard = new EDX.ExMesh[8];
                for (int i = 0; i < 8; i++)
                {
                    mSelCard[i] = EDX.LoadMesh(Application.StartupPath + "\\Data\\Models\\SelectCard.X", Application.StartupPath + "\\Data\\Models", false);
                    mSelCard[i].mt[0].Diffuse = Color.Green;
                    DrawLoad(1);
                }
                mTable = EDX.LoadMesh(Application.StartupPath + "\\Data\\Models\\Table.X", Application.StartupPath + "\\Data\\Models", false);
                DrawLoad(12);
                mT1 = EDX.LoadMesh(Application.StartupPath + "\\Data\\Models\\T1.X", Application.StartupPath + "\\Data\\Models", false);
                mT2 = EDX.LoadMesh(Application.StartupPath + "\\Data\\Models\\T2.X", Application.StartupPath + "\\Data\\Models", false);
                DrawLoad(2);
                mT3 = EDX.LoadMesh(Application.StartupPath + "\\Data\\Models\\T3.X", Application.StartupPath + "\\Data\\Models", false);
                mFlag = EDX.LoadMesh(Application.StartupPath + "\\Data\\Models\\Flag.X", Application.StartupPath + "\\Data\\Models", false);
                DrawLoad(2);
                mT1.mt[0].Diffuse = Color.FromArgb(70, Color.Aqua);
                mT2.mt[0].Diffuse = Color.FromArgb(70, Color.Aqua);
                mT3.mt[0].Diffuse = Color.FromArgb(70, Color.Aqua);

                for (int i = 1; i < 32; i++)
                {
                    mCard[i].mt[0].Diffuse = Color.White;
                    mCard[i].mt[0] = EDX.CreateMaterial(clrNone, clrNone, clrNone, clrNone, 0);
                    mCard[i].tx[0] = TextureLoader.FromFile(dvc, Application.StartupPath + "\\Data\\Models\\CardsTx\\" + i.ToString("00") + ".png");
                    DrawLoad(1.4f);
                }

                mWin = new ESprite(dvc, Application.StartupPath + "\\Data\\Models\\Win.png");
                DrawLoad(8f);

                mSelPl = EDX.LoadMesh(Application.StartupPath + "\\Data\\Models\\SelectedPl.X", Application.StartupPath + "\\Data\\Models", false);
                fnt = new Microsoft.DirectX.Direct3D.Font(dvc, new System.Drawing.Font("Arial", 10, FontStyle.Bold));
                fntWin = new Microsoft.DirectX.Direct3D.Font(dvc, new System.Drawing.Font("Arial", 20, FontStyle.Bold));

                // устанавливаем камеру
                Cam = new ECamera(dvc, new Vector3(), 70, 0, (float)panel1.Width / (float)panel1.Height, AngVis, 1, 1000);
                SetPos(PosCard[Ipl]);

                // говорим, что загрузка завершена
                EndLoad();

                // вызываем событие сброса
                OnDeviceReset(dvc, null);

                //Cursor.Hide();
                txbSendChat.Focus();
            }
            catch (Exception ex) { MessageBox.Show("Ошибка InitializeGraphics: " + ex.Message); }
        }

        void OnResizeDvc(Object sender, CancelEventArgs e)
        {
            if (WindowState == FormWindowState.Minimized) e.Cancel = true;
        }

        private void OnDeviceReset(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized) return;

            //FilterTextures();
            #region *** прозрачность *************************
            dvc.RenderState.AlphaBlendEnable = true;
            dvc.RenderState.DestinationBlend = Blend.InvSourceAlpha;
            dvc.RenderState.SourceBlend = Blend.SourceAlpha;
            //dvc.RenderState.ReferenceAlpha = 100;//Здесь пробовал числа от 0 до 255;
            //dvc.RenderState.AlphaFunction = Compare.Always; 
            #endregion *** прозрачность *************************

            // устанавливаем свет
            dvc.Lights[0].Type = LightType.Point; // тип освещения
            dvc.Lights[0].Position = new Vector3(0, 76.2f, 1); // положение
            dvc.Lights[0].Diffuse = Color.Wheat; // цвет
            dvc.Lights[0].Range = 150; // максимальное расстояние распространения света
            dvc.Lights[0].Attenuation0 = 0; // затухание
            dvc.Lights[0].Attenuation1 = 0;
            dvc.Lights[0].Attenuation2 = 0.00017f;
            dvc.Lights[0].Enabled = true;

            dvc.Lights[1].Type = LightType.Point; // тип освещения
            dvc.Lights[1].Position = new Vector3(stepXPl * 3f, 76.2f, 1); // положение
            dvc.Lights[1].Diffuse = Color.Wheat; // цвет
            dvc.Lights[1].Range = 150; // максимальное расстояние распространения света
            dvc.Lights[1].Attenuation0 = 0; // затухание
            dvc.Lights[1].Attenuation1 = 0;
            dvc.Lights[1].Attenuation2 = 0.00017f;
            dvc.Lights[1].Enabled = true;

            Cam.ResetCam();

        }

        // фильтрация текстур для сглаживания
        void FilterTextures()
        {
            // Try to set up a texture minify filter, pick anisotropic first
            if (dvc.DeviceCaps.TextureFilterCaps.SupportsMinifyAnisotropic)
            {
                dvc.SamplerState[0].MinFilter = TextureFilter.Anisotropic;
            }
            else if (dvc.DeviceCaps.TextureFilterCaps.SupportsMinifyLinear)
            {
                dvc.SamplerState[0].MinFilter = TextureFilter.Linear;
            }
            // Do the same thing for magnify filter
            if (dvc.DeviceCaps.TextureFilterCaps.SupportsMagnifyAnisotropic)
            {
                dvc.SamplerState[0].MagFilter = TextureFilter.Anisotropic;
            }
            else if (dvc.DeviceCaps.TextureFilterCaps.SupportsMagnifyLinear)
            {
                dvc.SamplerState[0].MagFilter = TextureFilter.Linear;
            }
        }

        #endregion *** инициализация графики *********************************************

        #region *** LoadSave **************************************************

        /*void LoadGame()
        {
            if (!File.Exists(fPathSave)) return;

            pl.Clear();
            pack.Clear();
            GC.Collect();

            EStream st = new EStream(fPathSave);
            st.OpenLoad();

            int kolVoPl = st.LoadInt();
            for (int i = 0; i < kolVoPl; i++)
            {
                pl.Add(new Playar(st.LoadStr()));
                int kolVoCrd = st.LoadInt();
                for (int c = 0; c < kolVoCrd; c++)
                {
                    pl[i].card.Add(LoadCard(st));
                }
                for (int t = 0; t < 5; t++)
                {
                    int kolVoC = st.LoadInt();
                    for (int c = 0; c < kolVoC; c++)
                    {
                        pl[i].town[t].card.Add(LoadCard(st));
                    }
                }
            }

            // колода
            int kolVoP = st.LoadInt();
            for (int i = 0; i < kolVoP; i++)
            {
                SaveCard(st, pack[i]);
            }

            st.CloseLoad();
        }

        Card LoadCard(EStream st)
        {
            Card crd = new Card();
            for (int j = 0; j < 5; j++)
                crd.troop[j] = st.LoadBool();
            return crd;
        }

        void SaveGame()
        {
            EStream st = new EStream(fPathSave);
            st.OpenSave();
            // игроки
            st.SaveInt(pl.Count);
            for (int i = 0; i < pl.Count; i++)
            {
                st.SaveStr(pl[i].name);
                st.SaveInt(pl[i].card.Count);
                for (int c = 0; c < pl[i].card.Count; c++)
                {
                    SaveCard(st, pl[i].card[c]);
                }
                for (int t = 0; t < 5; t++)
                {
                    st.SaveInt(pl[i].town[t].card.Count);
                    for (int c = 0; c < pl[i].town[t].card.Count; c++)
                    {
                        SaveCard(st, pl[i].town[t].card[c]);
                    }
                }
            }
            // колода
            st.SaveInt(pack.Count);
            for (int i = 0; i < pack.Count; i++)
            {
                SaveCard(st, pack[i]);
            }

            st.CloseSave();
        }

        void SaveCard(EStream st, Card c)
        {
            for (int j = 0; j < 5; j++)
                st.SaveBool(c.troop[j]);
        }
        */
        #endregion *** LoadSave **************************************************

        #region *** отрисовка **********************************************
        float fpsSum = 0;
        int fps = 0;
        private void timer1_Tick(object sender, EventArgs e)
        {
            if (!Visible) return;
            // проверка видимости сервера
            if (!bGame)
            {
                if (!frm.serverIsAvailable && !lblSrvNotAv.Visible)
                    lblSrvNotAv.Visible = true;
                if (frm.serverIsAvailable && lblSrvNotAv.Visible)
                    lblSrvNotAv.Visible = false;
                return;
            }
            else
            {
                if (!txbSendChat.Focused) txbSendChat.Focus();
            }

            if (WindowState == FormWindowState.Minimized) return;

            UpdateFrame();
            #region *** рисование ********************
            // очищаем окно с заливкой цветом CornflowerBlue
            // EDX.ClearDraw(System.Drawing.Color.CornflowerBlue);
            EDX.ClearDraw(System.Drawing.Color.Black);
            dvc.BeginScene();
            //-----------------------------

            // стол 
            dvc.Transform.World = Matrix.Translation(stepXPl * 2 - 20, -5.1f, 0);
            EDX.DrawMesh(mTable);
            // карты
            for (int p = 0; p < pl.Count; p++)
            {
                int xPl = p / 2;
                for (int t = 0; t < 5; t++)
                {
                    for (int c = 0; c < pl[p].town[t].card.Count; c++)
                    {
                        float tx = t * 6 - 12 + stepXPl * xPl;
                        if (p % 2 == 0)
                        {
                            dvc.Transform.World = Matrix.RotationX((float)(-0.01)) * Matrix.Translation(GetPosCard(p, t, c));
                            EDX.DrawMesh(mCard[pl[p].town[t].card[c].num]);
                        }
                        else
                        {
                            dvc.Transform.World = Matrix.RotationY((float)(Math.PI)) * Matrix.RotationX((float)(0.01f)) * Matrix.Translation(GetPosCard(p, t, c));
                            EDX.DrawMesh(mCard[pl[p].town[t].card[c].num]);
                        }

                        // последняя карта
                        if (t == pl[p].LastCard.X && c == pl[p].LastCard.Y && pl[p].cntTimeLastCard < 4)
                        {
                            Matrix tmpMx = new Matrix();
                            if (p % 2 == 1) tmpMx = Matrix.RotationY((float)(Math.PI)) * Matrix.RotationX((float)(0.01));
                            else tmpMx = Matrix.RotationX((float)(-0.01));
                            dvc.Transform.World = tmpMx * Matrix.Translation(GetPosCard(p, t, c)) * Matrix.Translation(0, 0.05f, 0); ;
                            EDX.DrawMesh(mSelCard[p]);
                            pl[p].cntTimeLastCard += elTime;
                            int A = (int)((4f - pl[p].cntTimeLastCard) * 100f / 4f);
                            if (A < 0) A = 0; if (A > 255) A = 255;
                            mSelCard[p].mt[0].Diffuse = Color.FromArgb(A, mSelCard[p].mt[0].Diffuse);
                        }
                    }
                }
                // рисуем имена около карточек с городами
                DrawName(p);
            }

            // последняя карта
            /*if (sCard.c != -1)
            {
                if (sCard.p % 2 == 1)
                    dvc.Transform.World = Matrix.RotationY((float)(Math.PI)) * Matrix.RotationX((float)(0.01));
                else dvc.Transform.World = Matrix.RotationX((float)(-0.01));
                dvc.Transform.World *= Matrix.Translation(GetPosCard(sCard.p, sCard.t, sCard.c)) * Matrix.Translation(0, 0.05f, 0); ;
              
                EDX.DrawMesh(mSelCard);
            }*/


            #region *** выбранный город и лидеры *************************************************
            if (SelTown != -1)
            {
                float x = Ipl / 2 * stepXPl - 9 + SelTown * 4.5f;
                float z = -1.5f;
                if (Ipl % 2 == 1) z = 1.5f;
                dvc.Transform.World = Matrix.Translation(x, 0.5f, z);
                EDX.DrawMesh(mSelTown);
            }

            // лидер
            List<int>[] ldr = Gl.LeaderMap(pl);
            for (int t = 0; t < 5; t++)
            {
                // пробегаемся по всем лидерам в этом городе
                for (int i = 0; i < ldr[t].Count; i++)
                {
                    float x = ldr[t][i] / 2 * stepXPl - 9 + t * 4.5f;
                    float z = -1.5f;
                    if (ldr[t][i] % 2 == 1) z = 1.5f;
                    dvc.Transform.World = Matrix.Translation(x, 0.1f, z);
                    EDX.DrawMesh(mFlag);
                }
            }

            #endregion *** выбранный город *************************************************


            #region  *** инфа *******************************************************

            if (countFrame % 10 == 0)
            {
                fps = (int)(1 / (fpsSum / 10f));
                fpsSum = elTime;
            }
            else fpsSum += elTime;
            if (bShowInfo)
            {
                fnt.DrawText(null, "FPS " + fps.ToString("F0"), new Rectangle(1, 15, 0, 0), DrawTextFormat.NoClip, Color.White);
                fnt.DrawText(null, "Выбранный игрок " + sCard.p.ToString(), new Rectangle(1, 30, 0, 0), DrawTextFormat.NoClip, Color.White);
                fnt.DrawText(null, "Выбранный город " + sCard.t.ToString(), new Rectangle(1, 45, 0, 0), DrawTextFormat.NoClip, Color.White);
                fnt.DrawText(null, "Выбранная карта " + sCard.c.ToString(), new Rectangle(1, 60, 0, 0), DrawTextFormat.NoClip, Color.White);
                Vector3 mm = Cam.Conv2DTo3D(ECamera.StandAxis.Y, mouseP.X, mouseP.Y);
                fnt.DrawText(null, "Координаты мыши " + mm.X.ToString() + ":" + mm.Z.ToString(), new Rectangle(1, 75, 0, 0), DrawTextFormat.NoClip, Color.White);
                fnt.DrawText(null, "ПозКам " + Cam.Pos.X.ToString() + ":" + Cam.Pos.Y.ToString() + ":" + Cam.Pos.Z.ToString() + ":" + Cam.AngX.ToString() + ":" + Cam.AngY.ToString() + "::" + Cam.AngleVis.ToString(), new Rectangle(1, 90, 0, 0), DrawTextFormat.NoClip, Color.White);
                fnt.DrawText(null, "IdQuery " + idQuery.ToString(), new Rectangle(1, 105, 0, 0), DrawTextFormat.NoClip, Color.White);
                fnt.DrawText(null, clrSelPl.A.ToString(), new Rectangle(1, 130, 0, 0), DrawTextFormat.NoClip, Color.White);
            }

            if (!frm.serverIsAvailable)
            {
                fnt.DrawText(null, "!!!СЕРВЕР НЕДОСТУПЕН!!!", new Rectangle(1, 0, 0, 0), DrawTextFormat.NoClip, Color.White);
                //if (btnSendChat.Enabled) btnSendChat.Enabled = false;
            }
            else
                if (!btnSendChat.Enabled)
                {
                    btnSendChat.Enabled = true;
                }
            #endregion  *** инфа *******************************************************

            // рисуем курсор
            //if (!cmd[0] && bInPanel) crs.Draw(mouseP.X, mouseP.Y);

            if (gmL[actG].bGameOver) RunGameOver();

            dvc.EndScene();
            //-----------

            dvc.Present();
            #endregion *** рисование ********************

        }

        void DrawName(int indPers)
        {
            int xPl = indPers / 2;

            if (indPers % 2 == 0)
            {
                dvc.Transform.World = Matrix.Translation(stepXPl * xPl, 0, -1.5f);
                EDX.DrawMesh(mTown);
                Vector2 v2 = Cam.Conv3DTo2D(new Vector3(stepXPl * xPl + stepXPl / 4, 0, -2f), panel1.Width, panel1.Height);
                if (Ipl % 2 == 1)
                    v2 = Cam.Conv3DTo2D(new Vector3(stepXPl * xPl - stepXPl / 4, 0, -1f), panel1.Width, panel1.Height);
                if (indPers == playPl)
                {
                    fnt.DrawText(null, pl[indPers].Name, new Rectangle((int)v2.X, (int)v2.Y - 14, 0, 0), DrawTextFormat.NoClip, Color.Thistle);
                    mSelPl.mt[0].Diffuse = clrSelPl;
                    EDX.DrawMesh(mSelPl);
                }
                else
                    fnt.DrawText(null, pl[indPers].Name, new Rectangle((int)v2.X, (int)v2.Y - 14, 0, 0), DrawTextFormat.NoClip, Color.LightGray);
            }
            else
            {
                dvc.Transform.World = Matrix.Translation(stepXPl * xPl, 0, 1.5f);
                EDX.DrawMesh(mTown1);
                Vector2 v2 = Cam.Conv3DTo2D(new Vector3(stepXPl * xPl + stepXPl / 4, 0, 1f), panel1.Width, panel1.Height);
                if (Ipl % 2 == 1)
                    v2 = Cam.Conv3DTo2D(new Vector3(stepXPl * xPl - stepXPl / 4, 0, 2), panel1.Width, panel1.Height);
                if (indPers == playPl)
                {
                    fnt.DrawText(null, pl[indPers].Name, new Rectangle((int)v2.X, (int)v2.Y - 14, 0, 0), DrawTextFormat.NoClip, Color.Thistle);
                    mSelPl.mt[0].Diffuse = clrSelPl;
                    EDX.DrawMesh(mSelPl);
                }
                else
                    fnt.DrawText(null, pl[indPers].Name, new Rectangle((int)v2.X, (int)v2.Y - 14, 0, 0), DrawTextFormat.NoClip, Color.LightGray);
            }

        }

        void DrawRect(float w, float h, float thX, float thY, Color clr)
        {
            Material m = new Material();
            m.Diffuse = clr;
            dvc.Material = m;

            Mesh[] msh = new Mesh[2];
            msh[0] = Mesh.Box(dvc, w, thY, thX);
            msh[1] = Mesh.Box(dvc, thX, thY, h - w * 2f);

            Matrix mx = dvc.Transform.World;

            dvc.Transform.World = mx * Matrix.Translation(-w / 2f, 0, -h / 2f);
            msh[0].DrawSubset(0);
            dvc.Transform.World = mx * Matrix.Translation(-w / 2f, 0, h / 2f);
            msh[0].DrawSubset(2);
            dvc.Transform.World = mx * Matrix.Translation(-w / 2f, 0, -h / 2f);
            msh[0].DrawSubset(1);
            dvc.Transform.World = mx * Matrix.Translation(w / 2f, 0, h / 2f);
            msh[0].DrawSubset(3);
        }

        void DrawString3D(String str, Vector3 pnt)
        {
            /* Mesh mesh = Mesh.TextFromFont(dvc, new System.Drawing.Font("Arial", 14.0f), str, 0.01f, 0.1f);
             // Create a material for our text mesh
             Material meshMaterial = new Material();
             meshMaterial.Diffuse = Color.Peru;
             dvc.Material = meshMaterial;
             dvc.Transform.World = Matrix.Translation(pnt);
             mesh.DrawSubset(0);*/
        }

        void DrawMyCards()
        {
            if (WindowState == FormWindowState.Minimized) return;

            if (im.Image != null) im.Image.Dispose();
            im.Image = new Bitmap(im.Width, im.Height);
            Graphics gr = Graphics.FromImage(im.Image);
            gr.Clear(Color.Black);
            for (int i = 0; i < pl[Ipl].card.Count; i++)
            {
                int idBmpCard = pl[Ipl].card[i].num;
                if (idBmpCard != -1)
                {
                    gr.DrawImage(new Bitmap(bmpCard[idBmpCard], 120, 180), 10, i * stepYCrd + shYCrd);
                }
            }

            int p = 0;
            for (int i = 0; i < pl.Count; i++)
            {
                String nm = pl[p].Name;
                if (UserIsOnline(pl[p].Name) || pl[p].isBot) nm = "<<" + nm + ">>";
                if (p == playPl)
                {
                    gr.FillRectangle(Brushes.Thistle, 5, 400 + i * 15, im.Width - 10, 15);
                    gr.DrawString(nm, Font, Brushes.Navy, 10, 400 + i * 15);
                }
                else gr.DrawString(nm, Font, Brushes.Thistle, 10, 400 + i * 15);
                p = NextPl(p);
            }

            if (bWait)
            {
                Brush br = new SolidBrush(Color.FromArgb(100, 255, 255, 255));
                gr.FillRectangle(br, 0, 0, im.Width, im.Height);
                br.Dispose();
            }
            if (gmL[actG].bGameOver)
            {
                int A = (int)(gmL[actG].TimeWin * 30); if (A > 90) A = 90;
                Brush br = new SolidBrush(Color.FromArgb(A, 255, 255, 255));
                gr.FillRectangle(br, 0, 0, im.Width, im.Height);
                br.Dispose();
            }

            im.Refresh();
            gr.Dispose();
        }

        void DrawChatList()
        {
            if (WindowState == FormWindowState.Minimized) return;

            if (imChatList.Image != null) imChatList.Image.Dispose();
            imChatList.Image = new Bitmap(imChatList.Width, imChatList.Height);
            Graphics gr = Graphics.FromImage(imChatList.Image);
            gr.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
            gr.Clear(Color.FromArgb(233, 230, 245));
            gr.DrawRectangle(Pens.SlateBlue, -1, 0, imChatList.Width, imChatList.Height - 1);
            for (int i = 0; i < ul.Count; i++)
            {
                TextRenderer.DrawText(gr, ul[i], Font, new Point(2, 3 + i * 15), Color.Black);
            }
            gr.Dispose();
            imChatList.Refresh();
        }

        void DrawSelectedCard(int p, int t, int c)
        {
            int n = countFrame % 10;
            float x = stepXPl * p - 6 + t * 6; // центр карты
            float z = 0; // центр карты
            if (p % 2 == 0) z = -c * stepZCard - 6;
            else z = c * stepZCard + 9;

        }

        #region *** GameOver **********************************************

        void RunGameOver()
        {
            if (!gmL[actG].bGameOver) return;

            gmL[actG].TimeWin += elTime;
            if (gmL[actG].TimeWin > 10)
            {
                gmL[actG].TimeWin = 10;
            }
            // делаем панарамный вид
            if (!bPanaram)
            {
                SetMovePanaram();
                gmL[actG].TimeWin = 0;
                return;
            }
            if (CamIsMove)
            {
                gmL[actG].TimeWin = 0;
                return;
            }

            // затмеваем область
            if (gmL[actG].TimeWin < 3)
            {
                mWin.SetColor(Color.FromArgb((int)(gmL[actG].TimeWin * 30), 255, 255, 255));
                DrawMyCards();
            }
            mWin.DrawR(0, 0, panel1.Width, panel1.Height);

            if (gmL[actG].TimeWin < 3) return;

            // последовательно выводим места
            int maxI = (int)((gmL[actG].TimeWin - 3) * 2.0f);
            List<Game.Lead> leadL = gmL[actG].leadL;
            if (maxI > leadL.Count) maxI = leadL.Count;

            for (int i = 0; i < maxI; i++)
            {
                String s = (i + 1).ToString() + "-е место (" + leadL[i].KolVoTowns.ToString();
                if (leadL[i].KolVoTowns == 0 || leadL[i].KolVoTowns == 5) s += " городов):";
                else if (leadL[i].KolVoTowns == 1) s += " город):";
                else s += " города):";

                for (int j = 0; j < leadL[i].idPl.Count; j++)
                {
                    s += " " + pl[leadL[i].idPl[j]].Name;
                    if (j < (leadL[i].idPl.Count - 2)) s += ",";
                    if (j == (leadL[i].idPl.Count - 2)) s += " и";
                }
                fntWin.DrawText(null, s, new Rectangle(50, 50 + i * 40, 0, 0), DrawTextFormat.NoClip, Color.Navy);
            }
        }

        #endregion *** GameOver **********************************************

        float percentLoad = 0;
        System.Drawing.Font fntLoad = new System.Drawing.Font("Arial", 14, FontStyle.Bold);
        void DrawLoad(float addPercent)
        {
            if (imG.Image != null) imG.Image.Dispose();
            imG.Image = new Bitmap(imG.Width, imG.Height);
            Graphics gr = Graphics.FromImage(imG.Image);
            percentLoad += addPercent;
            if (percentLoad > 100) percentLoad = 100;
            gr.Clear(Color.White);
            gr.FillRectangle(Brushes.SkyBlue, 0, 0, percentLoad * (imG.Width / 100.0f), imG.Height);
            TextRenderer.DrawText(gr, "Загрузка моделей " + percentLoad.ToString("F0") + "%", fntLoad, new Rectangle(0, 0, imG.Width, imG.Height), Color.Blue);
            imG.Refresh();
            gr.Dispose();
            Application.DoEvents();
            idQuery = -1000;
        }
        void EndLoad()
        {
            idQuery = 0;
            imG.Visible = false;
            SetGameStatus(true);
        }

        #endregion *** отрисовка **********************************************

        #region *** события игры ********************************************

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            // чат
            if (sender == txbSendChat)
            {
                if (e.Control && e.KeyCode == Keys.Enter)
                {
                    btnSendChat_Click(null, null);
                    e.SuppressKeyPress = true;
                    return;
                }
            }

            if (!bGame) return;

            if (gmL[actG].bGameOver) return;

            // инфа
            if (e.KeyCode == Keys.End) bShowInfo = !bShowInfo;

            // отдаление / приближение
            if (e.KeyCode == Keys.Home)
            {
                if (!bPanaram) SetMoveCard();
                else SetMovePanaram();
            }

            if (e.Control && e.KeyCode == Keys.Up && !bPanaram) SetMovePanaram();
            if (e.Control && e.KeyCode == Keys.Down && bPanaram) SetMoveCard();

            cmd[0] = false;
            if (e.Control && e.Alt)
            {
                cmd[0] = true;
                Cursor.Position = panel1.PointToScreen(PanelC);
            }

            // команды движения
            if (e.Control && e.KeyCode == Keys.Left) cmd[1] = true;
            if (e.Control && e.KeyCode == Keys.Right) cmd[2] = true;
            if (sender == txbSendChat && e.Control) e.SuppressKeyPress = true;
        }

        private void Form1_KeyUp(object sender, KeyEventArgs e)
        {
            if (!bGame) return;

            if (e.KeyCode == Keys.ControlKey)
            {
                if (bPanaram) SetMovePanaram();
                else SetMoveCard();
                cmd[0] = false;
                cmd[1] = false;
                cmd[2] = false;
            }

            if (e.Control && (e.KeyCode == Keys.Left || e.KeyCode == Keys.Right))
            {
                cmd[1] = false;
                cmd[2] = false;
            }
        }

        private void im_MouseDown(object sender, MouseEventArgs e)
        {
            if (!bGame) return;

            if (gmL[actG].bGameOver)
            {
                DrawMyCards();
                return;
            }

            // добавить карту в текущий город
            if (Ipl == playPl)
            {
                if (bWait) return;
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    if (SelTown != -1)
                    {
                        if (pl[Ipl].town[SelTown].card.Count < 4)
                        {
                            // находим карту в руке
                            int SelCardArm = (e.Y - shYCrd) / stepYCrd;
                            // если такой карты нет, выходим
                            if (SelCardArm < 0 || SelCardArm >= pl[Ipl].card.Count) return;

                            // отправляем на сервер запрос
                            frm.SendCardInTable(gmL[actG].Name, SelCardArm, SelTown);
                            SelTown = -1;
                            idQuery = 50;
                            DrawMyCards();
                        }
                    }
                }
            }



            if (e.Button == System.Windows.Forms.MouseButtons.Middle)
            {
                if (bPanaram) SetMoveCard();
                else SetMovePanaram();
            }

        }

        private void panel1_MouseDown(object sender, MouseEventArgs e)
        {
            if (!bGame) return;

            if (gmL[actG].bGameOver) return;

            DetectActCard(e.X, e.Y);

            if (e.Button == MouseButtons.Left)
            {
                // выбор города для добавления карты
                if (sCard.p == Ipl && sCard.t != -1) //  && !bPanaram
                {
                    SelTown = sCard.t;
                }
                // выбор плеера для увеличения
                /*if (bPanaram)
                {
                    if (sCard.p != -1)
                    {
                        SetMove(PosCard[sCard.p]);
                        bPanaram = false;
                    }
                }*/
            }

            // отдаление плеера по клику колесика
            if (e.Button == System.Windows.Forms.MouseButtons.Middle)
            {
                if (bPanaram) SetMoveCard();
                else SetMovePanaram();
            }

            if (e.Button == MouseButtons.Right)
            {
                cmd[0] = true;
                Cursor.Position = panel1.PointToScreen(PanelC);
            }

        }

        private void panel1_MouseUp(object sender, MouseEventArgs e)
        {
            if (!bGame) return;

            if (cmd[0])
            {
                cmd[0] = false;
                if (bPanaram) SetMovePanaram();
                else SetMoveCard();
            }
        }

        private void panel1_MouseMove(object sender, MouseEventArgs e)
        {
            if (!bGame) return;

            if (gmL[actG].bGameOver) return;

            DetectActCard(e.X, e.Y);

            /* if (cmd[8])
             {
                 if (e.X != PanelC.X || e.Y != PanelC.Y)
                 {
                     DiffX += (e.X - PanelC.X);
                     DiffY += (e.Y - PanelC.Y);

                     Cam.AngX += (DiffY / elTime) / 400f;
                     Cam.AngY -= (DiffX / elTime) / 400f;
                    
                     if (PanelC.X < e.X) Cam.AngY += ((e.X - PanelC.X) / elTime) / 400f;
                     if (PanelC.X > e.X) Cam.AngY -= ((PanelC.X - e.X) / elTime) / 400f;

                     if (PanelC.Y < e.Y) Cam.AngX += ((e.Y - PanelC.Y) / elTime) / 400f;
                     if (PanelC.Y > e.Y) Cam.AngX -= ((PanelC.Y - e.Y) / elTime) / 400f;

                     mouseP.X = e.X;
                     mouseP.Y = e.Y;
                 }
                 //Cursor.Position = panel1.PointToScreen(PanelC);
             }*/

            mouseP.X = e.X;
            mouseP.Y = e.Y;
        }

        void Frm_MouseWheel(object sender, MouseEventArgs e)
        {
            if (!bGame) return;

            if (gmL[actG].bGameOver) return;

            if (cmd[0])
            {
                if (e.Delta < 0)
                {
                    if (Cam.AngleVis < 60) Cam.AngleVis += 3;
                }
                else if (Cam.AngleVis > 10) Cam.AngleVis -= 3;
            }
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            if (!bGame) return;
            if (WindowState == FormWindowState.Minimized) return;
            PanelC = new System.Drawing.Point(panel1.Width / 2, panel1.Height / 2);
            DrawMyCards();
            Cam.AspectRatio = (float)panel1.Width / (float)panel1.Height;
        }

        private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            frm.Close();
        }

        #endregion *** события ********************************************

        #region *** разное для игры ******************************************

        void SetGameStatus(bool vis)
        {
            if (vis)
            {
                if (dvc == null)
                {
                    InitializeGraphics();
                }
                else
                {
                    if (bPanaram) SetPos(gmL[actG].PosPanaram[Ipl]);
                    else SetPos(PosCard[Ipl]);
                    pnlLoad.Visible = false;

                    tBtnUpdateTable.Visible = true;
                    tBtnShowLastCards.Visible = true;

                    txbSendChat.Focus();
                    bGame = true;
                }
            }
            else
            {
                pnlLoad.Visible = true;
                tBtnUpdateTable.Visible = false;
                tBtnShowLastCards.Visible = false;
                bGame = false;
            }
        }

        class DrawPrimitive
        {
            VertexBuffer vb = null;
            int sz;
            CustomVertex.PositionColored[] verts;

            Device dvc;

            public DrawPrimitive(Device device, int maxSize)
            {
                vb = new VertexBuffer(typeof(CustomVertex.PositionColored), maxSize, device, Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionColored.Format, Pool.Default);
                vb.Created += new EventHandler(this.OnVertexBufferCreate);
                sz = maxSize;
                dvc = device;
                // создаем массив точек
                verts = new CustomVertex.PositionColored[sz];
                OnVertexBufferCreate(vb, null);
            }

            void OnVertexBufferCreate(object sender, EventArgs e)
            {
                VertexBuffer buf = (VertexBuffer)sender;

                // создаем массив точек
                //CustomVertex.PositionColored[] verts = new CustomVertex.PositionColored[sz];

                // заполняем точки
                /*verts[0].Position = new Vector3(xPos, yPos, zPos);
                verts[i].Color = RandomColor.ToArgb();*/
                // заполняем буфер, начиная с точки (второе значение)
                buf.SetData(verts, 0, LockFlags.None);
            }

            public void DrawLine(Vector3 v1, Vector3 v2, Color clr)
            {
                verts[0].Position = v1;
                verts[0].Color = clr.ToArgb();
                verts[0].Position = v2;
                verts[0].Color = clr.ToArgb();
                OnVertexBufferCreate(vb, null);

                dvc.VertexFormat = CustomVertex.PositionColored.Format;
                dvc.SetStreamSource(0, vb, 0);
                /*// точки
                dvc.DrawPrimitives(PrimitiveType.PointList, 0, sz);*/
                // линии
                dvc.DrawPrimitives(PrimitiveType.LineList, 0, sz / 2);

                /*// LineStrip
                dvc.DrawPrimitives(PrimitiveType.LineStrip, 0, sz - 1);
                // TriangleList
                dvc.DrawPrimitives(PrimitiveType.TriangleList, 0, sz / 3);
                // TriangleStrip
                dvc.DrawPrimitives(PrimitiveType.TriangleStrip, 0, sz - 2);
                // TriangleFan
                dvc.DrawPrimitives(PrimitiveType.TriangleFan, 0, sz - 2);*/
            }

        }

        void UpdateFrame()
        {
            elTime = Utility.GetElapsedTime();

            // выделеный игрок
            if (bSelPlDown) cntSelPl -= elTime * 300;
            else cntSelPl += elTime * 100;
            if (cntSelPl < 0)
            {
                if (cntSelPl < -50)
                {
                    bSelPlDown = false;
                    cntSelPl = -50;
                }
                int a = (int)cntSelPl * -1;
                int Alf = (int)((a - (a * (a / 100.0))) * 2);
                clrSelPl = Color.FromArgb(Alf, 0, 0, 0);
            }
            else
            {

                if (cntSelPl > 50)
                {
                    bSelPlDown = true;
                    cntSelPl = 50;
                }
                int a = (int)cntSelPl;
                int Alf = (int)((a - (a * (a / 100.0))) * 2);
                clrSelPl = Color.FromArgb(Alf, 255, 255, 255);
            }

            if (CamIsMove)
            {
                CamIsMove = MoveCam();
            }

            float X = Cursor.Position.X;
            float Y = Cursor.Position.Y;

            if (cmd[0])
            {
                Cursor.Position = panel1.PointToScreen(PanelC);
                float dX = (X - Cursor.Position.X);
                float dY = (Y - Cursor.Position.Y);
                Cam.AngY += (dX / elTime) / 300f;
                Cam.AngX += (dY / elTime) / 300f;
            }

            if (Ipl % 2 == 0)
            {
                if (cmd[1]) if (Cam.X > 0) Cam.X--;
                if (cmd[2]) if (Cam.X < ((pl.Count - 1) / 2) * stepXPl) Cam.X++;
            }
            else
            {
                if (cmd[1]) if (Cam.X < ((pl.Count - 1) / 2) * stepXPl) Cam.X++;
                if (cmd[2]) if (Cam.X > 0) Cam.X--;
            }

            System.Drawing.Point min = panel1.PointToScreen(new System.Drawing.Point(0, 0));
            System.Drawing.Point max = panel1.PointToScreen(new System.Drawing.Point(panel1.Width, panel1.Height));

            bool bActInPanel = X >= min.X && Y >= min.Y && X < max.X && Y < max.Y;

            // если курсор был вне границ, а теперь в панели
            /*if (bActInPanel && !bInPanel)
                Cursor.Hide();*/

            // если курсор вне границ, а был в панели

            /*if (!bActInPanel && bInPanel)
                Cursor.Show();*/

            bInPanel = bActInPanel;

            #region *** ходит комп *******************************
            /*if (playPl != actPl && countFrame % 50 == 0)
            {

                // находим город, и карту
                Point p = pl[playPl].CardInTown();
                if (p.X != -1 && p.Y != -1)
                {
                    PlayCard(playPl, p.Y, p.X);
                }

                // меняем игрока
                playPl = NextPl(playPl);
                // перерисовываем список
                DrawMyCards();
            }*/
            #endregion *** ходит комп *******************************

            countFrame++;
        }

        int NextPl(int ActNum)
        {
            if (ActNum == 0) return ActNum + 1;
            // 2,3 игрока
            if (pl.Count <= 3)
            {
                ActNum++;
                if (ActNum >= pl.Count) ActNum -= pl.Count;
                return ActNum;
            }
            // 4 игрока
            if (pl.Count == 4)
            {
                if (ActNum == 1) return 3;
                if (ActNum == 3) return 2;
                if (ActNum == 2) return 0;
            }
            // 5 игроков
            if (pl.Count == 5)
            {
                if (ActNum == 1) return 3;
                if (ActNum == 3) return 4;
                if (ActNum == 4) return 2;
                if (ActNum == 2) return 0;
            }
            // 6 игроков
            if (pl.Count == 6)
            {
                if (ActNum == 1) return 3;
                if (ActNum == 3) return 5;
                if (ActNum == 5) return 4;
                if (ActNum == 4) return 2;
                if (ActNum == 2) return 0;
            }
            // 7 игроков
            if (pl.Count == 7)
            {
                if (ActNum == 1) return 3;
                if (ActNum == 3) return 5;
                if (ActNum == 5) return 6;
                if (ActNum == 6) return 4;
                if (ActNum == 4) return 2;
                if (ActNum == 2) return 0;
            }
            // 8 игроков
            if (pl.Count == 8)
            {
                if (ActNum == 1) return 3;
                if (ActNum == 3) return 5;
                if (ActNum == 5) return 7;
                if (ActNum == 7) return 6;
                if (ActNum == 6) return 4;
                if (ActNum == 4) return 2;
                if (ActNum == 2) return 0;
            }
            return 0;
        }

        bool MoveCam()
        {
            if (destP.Equal(Cam.Pos.X, Cam.Pos.Y, Cam.Pos.Z, Cam.AngX, Cam.AngY, Cam.AngleVis)) return false;
            Cam.Pos = new Vector3(Cam.Pos.X + speed.x * elTime, Cam.Pos.Y + speed.y * elTime, Cam.Pos.Z + speed.z * elTime);
            Cam.AngX += speed.angX * elTime;
            Cam.AngY += speed.angY * elTime;
            Cam.AngleVis += speed.angVis * elTime;
            if (Diff() > minDiff)
            {
                Cam.Pos = destP.pos;
                Cam.AngX = destP.angX;
                Cam.AngY = destP.angY;
                Cam.AngleVis = destP.angVis;
                return false;
            }
            minDiff = Diff();
            return true;
        }

        float Diff()
        {
            float ret = 0;
            ret += Math.Abs(Cam.Pos.X - destP.x);
            ret += Math.Abs(Cam.Pos.Y - destP.y);
            ret += Math.Abs(Cam.Pos.Z - destP.z);
            ret += Math.Abs(Cam.AngX - destP.angX);
            ret += Math.Abs(Cam.AngY - destP.angY);
            ret += Math.Abs(Cam.AngleVis - destP.angVis);
            return ret;
        }

        // указать новую точку для движения
        void SetMove(Position pos)
        {
            destP = pos;

            float n = Diff();
            n = (float)Math.Sqrt(n) / 20f;

            speed.x = (pos.x - Cam.Pos.X) / n;
            speed.y = (pos.y - Cam.Pos.Y) / n;
            speed.z = (pos.z - Cam.Pos.Z) / n;
            speed.angX = (pos.angX - Cam.AngX) / n;
            speed.angY = (pos.angY - Cam.AngY) / n;
            speed.angY = (pos.angY - Cam.AngY) / n;
            speed.angVis = (pos.angVis - Cam.AngleVis) / n;

            CamIsMove = true;

            minDiff = float.MaxValue;
        }

        void SetPos(Position pos)
        {
            Cam.Pos = pos.pos;
            Cam.AngX = pos.angX;
            Cam.AngY = pos.angY;
            Cam.AngleVis = pos.angVis;
        }

        void SetMovePanaram()
        {
            SetMove(gmL[actG].PosPanaram[Ipl]);
            bPanaram = true;
        }

        void SetMoveCard()
        {
            SetMove(PosCard[Ipl]);
            bPanaram = false;
        }

        bool DetectActTown(float mX, float mY)
        {
            Vector3 ms = Cam.Conv2DTo3D(ECamera.StandAxis.Y, mX, mY);
            int p = (int)(ms.X + 15) / (int)stepXPl;
            float X = ms.X - p * stepXPl;

            if (ms.Z > -3f && ms.Z < 0f)
            {
                if (X > -9f && X < 9f)
                {
                    float zz = 3f - (ms.Z);
                    if (X < 0) sCard.t = (int)((X + 15f + zz) / 6) - 1;
                    else sCard.t = (int)((X + 15f - zz) / 6) + 1;
                }
                else return false;
            }
            else if (ms.Z > 0f && ms.Z < 3f)
            {
                if (X > -9f && X < 9f)
                {
                    float zz = 3 - ms.Z;

                    if (X < 0) sCard.t = (int)(X + 15f - zz) / 6;
                    else sCard.t = (int)((X + 15f + zz) / 6);
                    sCard.t += 5;
                }
                else return false;

            }
            else return false;

            p *= 2;

            if (sCard.t > 4)
            {
                p += 1;
                sCard.t -= 5;
            }

            sCard.Set(p, sCard.t, -1);
            return true;
        }

        void DetectActCard(float mX, float mY)
        {
            // обнуляем
            sCard.Set(-1, -1, -1);

            // устанавливаем игрока

            // если курсор над городом
            // устанавливаем только город и выходим
            if (DetectActTown(mX, mY)) return;

            Vector3 ms = Cam.Conv2DTo3D(ECamera.StandAxis.Y, mX, mY);
            int p = -1;
            int t = -1;
            int c = -1;

            // если сторона 1
            if (ms.Z < -3)
            {
                Vector3 n = GetPosCard(0, 0, 0);
                if (ms.X + 15 >= 0)
                {
                    p = (int)((ms.X + 15) / stepXPl);
                    t = (int)((ms.X + 15 - p * stepXPl) / 6);
                    c = (int)(-(ms.Z + 3) / stepZCard);
                    p = p * 2;
                }
            }
            // если сторона 2
            else if (ms.Z > 3)
            {
                Vector3 n = GetPosCard(0, 0, 0);
                if (ms.X + 15 >= 0)
                {
                    p = (int)((ms.X + 15) / stepXPl);
                    t = (int)((ms.X + 15 - p * stepXPl) / 6);
                    c = (int)((ms.Z - 3) / stepZCard);
                    p = p * 2 + 1;
                }
            }

            //sCard.p = p;
            // проверяем карту и устанавливаем в случае правильности
            // если плеер существует, выбираем его
            if (p >= 0 && p < pl.Count)
            {
                sCard.p = p;
                if (t >= 0 && t < 5)
                {
                    if (c >= 0 && c < pl[p].town[t].card.Count)
                    {
                        sCard.Set(p, t, c);
                    }
                }
            }

        }

        Vector3 GetPosCard(int p, int t, int c)
        {
            Vector3 v = new Vector3();
            v.X = t * 6 - 12 + stepXPl * (p / 2); // центр карты
            v.Y = 0;
            if (p % 2 == 0)
                v.Z = -c * stepZCard - 4.5f - 3;
            else
                v.Z = c * stepZCard + 4.5f + 3;
            return v;
        }

        void PlaySound(int n)
        {
            try
            {
                if (frm.bSound) snd[n].Play();
            }
            catch { }
        }

        #endregion *** разное для игры ******************************************

        #region *** Коммуникации функции, вызывающиеся формой Conn*********************

        #region *** обновления *****************************************

        /// <summary>Обновить список подключенных юзеров.
        /// sl - список, ConnectionUser - имя новоподключенного юзера, если таковой имеется</summary>
        public void UpdateChatList(List<String> sl, bool bErr)
        {
            idQuery = 0;
            if (bErr)
            {
                MessageBox.Show("Ошибка обновления списка подключеных юзеров");
                return;
            }

            // сравниваем список юзеров
            // проверка на удаление
            for (int i = 0; i < ul.Count; i++)
            {
                bool bDel = true;
                for (int j = 0; j < sl.Count; j++)
                    if (ul[i] == sl[j])
                    {
                        bDel = false;
                        break;
                    }
                if (bDel) AddMessage("Сервер", "Ушел " + ul[i]);
            }
            // проверка на подключение
            for (int i = 0; i < sl.Count; i++)
            {
                bool bAdd = true;
                for (int j = 0; j < ul.Count; j++)
                    if (sl[i] == ul[j])
                    {
                        bAdd = false;
                        break;
                    }
                if (bAdd) AddMessage("Сервер", "Пришел " + sl[i]);
            }

            ul = sl;
            DrawChatList();
            if (bGame) DrawMyCards();

        }

        /// <summary>пришло новое сообщение от юзера в чат</summary>
        public void RecvNewUserMessage(String UserName, String Message, bool bErr)
        {
            idQuery = 0;
            if (bErr)
            {
                MessageBox.Show("Ошибка добавления сообщения в чат от " + UserName);
                return;
            }
            AddMessage(UserName, Message);
            // если это сообщение от себя
            if (UserName == frm.ClientName)
            {
                txbSendChat.Text = "";
            }
        }

        /// <summary>Обновить список игр. gmL - лист игр</summary>
        public void UpdateGameList(List<Game> gml, bool bErr)
        {
            idQuery = 0;
            if (bErr)
            {
                MessageBox.Show("Ошибка обновления листа игр");
                return;
            }

            #region *** сравниваем лист игр **********
            // смотрим, не было ли удалено какой-либо игры
            // пробегаемся по старому списку игр
            for (int i = 0; i < gmL.Count; i++)
            {
                String actN = gmL[i].Name; // имя теукущей игры
                int idNewGame = -1; // id текущей игры в новом списке
                // пробегаемся по новому списку игр
                for (int j = 0; j < gml.Count; j++)
                {
                    // если  в новом списке найдена игра, которыя уже была в старом списке
                    if (actN == gml[j].Name)
                    {
                        idNewGame = j;
                        // если в старом списке игра не запущена, а в новом запущена
                        if (!gmL[i].GameIsPlay && gml[j].GameIsPlay)
                        {
                            ;//AddMessage("Сервер", "Запущена игра '" + actN + "'");
                        }
                        break;
                    }
                }
                // если в новом списке не найдена текущая игра
                if (idNewGame == -1)
                {
                    //AddMessage("Сервер", "игра '" + actN + "' была удалена.");
                    // если это текущая игра игрока, выйти из игры
                    if (i == actG)
                    {
                        btnShowMenu_Click(null, null);
                        actG = -1;
                    }
                }
                else
                {
                    // смотрим, не был ли удален я-юзер из текущей игры
                    // пробегаемся по всем юзерам старого списка
                    for (int u = 0; u < gmL[i].pl.Count; u++)
                    {
                        // если в этой игре есть я-юзер
                        if (gmL[i].pl[u].Name == frm.ClientName)
                        {
                            bool bDel = true;
                            for (int n = 0; n < gml[idNewGame].pl.Count; n++)
                            {
                                if (gml[idNewGame].pl[n].Name == frm.ClientName)
                                {
                                    bDel = false;
                                    break;
                                }
                            }
                            if (bDel)
                            {
                                ;//AddMessage("Сервер", "Вы были удалены из игры '" + actN + "'.");
                            }
                        }
                    }
                }
            }

            // смотрим, не была ли добавлена какая-либо игра
            for (int i = 0; i < gml.Count; i++)
            {
                String actN = gml[i].Name;
                bool bAdd = true;
                for (int j = 0; j < gmL.Count; j++)
                {
                    if (actN == gmL[j].Name)
                    {
                        bAdd = false;
                        break;
                    }
                }
                if (bAdd)
                {
                    AddMessage("Сервер", "Добавлена игра '" + actN + "' модератор: " + gml[i].Moderator);
                }
            }
            #endregion *** сравниваем лист игр **********

            // запоминаем адрес старого списка
            List<Game> gmL1 = gmL;
            gmL = gml;

            // восстанавливаем
            for (int i = 0; i < gmL.Count; i++)
            {
                // если игра играется
                if (gmL[i].GameIsPlay)
                {
                    // ищем, была ли эта игра до этого
                    for (int j = 0; j < gmL1.Count; j++)
                    {
                        if (gmL[i].Name == gmL1[j].Name)
                        {
                            if (gmL1[j].GameIsPlay)
                                gmL[i].CopyGamePlayData(gmL1[j]);
                            break;
                        }
                    }
                    // смотрим, закончена ли игра
                    gmL[i].GameIsOver();
                }
            }

            RefillGameList();
            btnUpdateGameList.Enabled = true;
            idQuery = 0;
        }

        /// <summary>Обновить список ботов</summary>
        public void UpdateBotList()
        {
            RefillBotList();
        }

        public void UpdateTable(String GameName, List<Player> PL, Byte PlayPl, List<Card> myCard, bool bErr)
        {
            idQuery = 0;
            if (bErr)
            {
                MessageBox.Show("Ошибка обновления стола");
                return;
            }

            int idGame = -1;
            for (int i = 0; i < gmL.Count; i++)
            {
                if (GameName == gmL[i].Name)
                {
                    idGame = i;
                    break;
                }
            }

            if (idGame == -1) MessageBox.Show("пришло сообщение об обновлении положения карт в игре, которой нет в списке игр. Это БАГ O_o");
            if (PL.Count != gmL[idGame].pl.Count) MessageBox.Show("Количество игроков, пришедшее с сервера не равно количеству игроков в игре. Это БАГ O_o");

            // я в списке игроков (игрок, которым играет клиент)
            if (gmL[idGame].Ipl == -1)
            {
                int ipl = -1;
                for (int i = 0; i < gmL[idGame].pl.Count; i++)
                {
                    if (gmL[idGame].pl[i].Name == frm.ClientName)
                    {
                        ipl = i;
                        break;
                    }
                }
                if (ipl == -1) MessageBox.Show("Не найден номер игрока,которым играет клиент. БАГ O_o");
                else gmL[idGame].Ipl = ipl;
            }

            // запоминаем все, что не приходит с обновленным столом
            float[] cntZat = new float[PL.Count]; // запоминаем старые счетчики затухания
            Point[] lc = new Point[PL.Count]; // запоминаем старые последние карты
            String[] sL = new String[PL.Count]; // запоминаем старые имена
            bool bPan = gmL[idGame].bPanaram;// запоминаем вид камеры
            int[] Bot = new int[PL.Count];// запоминаем ботов
            for (int i = 0; i < PL.Count; i++)
            {
                cntZat[i] = gmL[idGame].pl[i].cntTimeLastCard;
                lc[i] = gmL[idGame].pl[i].LastCard;
                sL[i] = gmL[idGame].pl[i].Name;
                Bot[i] = gmL[idGame].pl[i].botInd;
            }
            // копируем
            if (idGame == actG && PlayPl != Ipl) CardInTable(gmL[idGame].pl, PL);
            gmL[idGame].pl = PL;
            gmL[idGame].pl[gmL[idGame].Ipl].card = myCard;
            // восстанавливаем значения счетчиков затухания и имена
            gmL[idGame].bPanaram = bPan;
            for (int i = 0; i < PL.Count; i++)
            {
                // если последняя карта не поменялась, восстанавливаем для нее счетчик затухания
                if (gmL[idGame].pl[i].LastCard.X == lc[i].X && gmL[idGame].pl[i].LastCard.Y == lc[i].Y)
                {
                    gmL[idGame].pl[i].cntTimeLastCard = cntZat[i];
                }
                // восстанавливаем имя
                gmL[idGame].pl[i].Name = sL[i];
                // восстанавливае ботов
                gmL[idGame].pl[i].botInd = Bot[i];
            }

            // игрок, который ходит в данный момент
            gmL[idGame].PlayPl = PlayPl;

            gmL[idGame].GameIsOver();

            // если idGame - текущая игра, или текущей игры нет вовсе, обновляем данные
            if (actG == idGame || actG == -1)
                actG = idGame;

            if (EnterIdGame == idGame)
            {
                // если необходимо войти не в текущую игру
                if (actG != idGame)
                {
                    actG = idGame;
                }
                // входим в игру
                SetGameStatus(true);
                EnterIdGame = -1;
            }

            DrawMyCards();

            if (Ipl == playPl)
                PlaySound(2);

        }

        bool CardInTable(List<Player> lPl, List<Player> nPl)
        {
            for (int i = 0; i < lPl.Count; i++)
            {
                for (int t = 0; t < 5; t++)
                    if (lPl[i].town[t].card.Count != nPl[i].town[t].card.Count)
                    {
                        snd[3].Play();
                        return true;
                    }
            }
            return false;
        }

        bool UserIsOnline(String uName)
        {
            for (int i = 0; i < ul.Count; i++)
                if (uName == ul[i]) return true;
            return false;
        }

        int GetIndGameByName(String GameName)
        {
            if (GameName == "") return -1;
            for (int i = 0; i < gmL.Count; i++)
                if (gmL[i].Name == GameName)
                    return i;
            return -1;
        }

        /*int GetIndPlayerByName(int indGame, String PlayerName)
        {
            if (PlayerName == "") return -1;
            for (int i = 0; i < gmL[indGame].pl.Count; i++)
                if (gmL[indGame].pl[i].Name == PlayerName)
                    return i;
            return -1;
        }*/

        #endregion *** обновления *****************************************

        #region *** ответы ********************************************************

        public void CreateGame(byte ansv)
        {
            if (ansv == 0) RefillGameList();
            if (ansv == 1) MessageBox.Show("Такое имя игры уже существует");
            if (ansv == 255) MessageBox.Show("Ошибка в ответе от сервера на запрос: создание игры");
            idQuery = 0;
        }

        public void BeginGame(byte ansv)
        {
            idQuery = 0;
            if (bGame) return;
            if (ansv == 0)
            {
                RefillGameTabs();
            }
            if (ansv == 1) MessageBox.Show("Такой игры уже не существует");
            if (ansv == 2) MessageBox.Show("Невозможно запустить игру, поскольку не все игроки online");
            if (ansv == 3) MessageBox.Show("В игре должно быть минимум 2 игрока");
            if (ansv == 255) MessageBox.Show("Ошибка в ответе от сервера на запрос: запуск игры");
        }

        public void DeleteGame(byte ansv)
        {
            if (ansv == 0) RefillGameList();
            if (ansv == 1) MessageBox.Show("Такой игры уже не существует");
            if (ansv == 255) MessageBox.Show("Ошибка в ответе от сервера на запрос: удаление игры");
            idQuery = 0;
        }

        public void DeleteUserInGame(byte ansv)
        {
            if (ansv == 0) RefillGameList();
            if (ansv == 1) MessageBox.Show("Такой игры уже не существует");
            if (ansv == 2) MessageBox.Show("Такого игрока в этой игре уже не существует");
            if (ansv == 255) MessageBox.Show("Ошибка в ответе от сервера на запрос: удалить юзера из игры");
            idQuery = 0;
        }

        public void EnterIntoGame(byte ansv)
        {
            if (ansv == 0) RefillGameList();
            if (ansv == 1) MessageBox.Show("Невозможно вступись в эту игру: Такой игры уже не существует");
            if (ansv == 2) MessageBox.Show("Вы уже присутствуете в этой игре");
            if (ansv == 3) MessageBox.Show("Невозможно вступись в эту игру: В игре уже максимально-возможное количество человек");
            if (ansv == 255) MessageBox.Show("Ошибка в ответе от сервера на запрос: подать завку на участие");
            idQuery = 0;
        }

        public void CanselIntoGame(byte ansv)
        {
            if (ansv == 0) RefillGameList();
            if (ansv == 1) MessageBox.Show("Такой игры уже не существует");
            if (ansv == 255) MessageBox.Show("Ошибка в ответе от сервера на запрос: Отменить заявку на участие");
            idQuery = 0;
        }

        public void PlayCard(byte ansv)
        {
            if (ansv == 0) DrawMyCards();
            if (ansv == 1) MessageBox.Show("Такой игры уже не существует");
            if (ansv == 2) MessageBox.Show("Вы уже не в этой игре");
            if (ansv == 3) MessageBox.Show("Такой карты в вашей руке нет");
            if (ansv == 4) MessageBox.Show("Не выбран город");
            if (ansv == 5) MessageBox.Show("В городе уже максимальное количество карт");
            if (ansv == 6) MessageBox.Show("Вы не можете выложить карту, так как сейчас не ваш ход");
            if (ansv == 255) MessageBox.Show("Ошибка в ответе от сервера на запрос: выложить карту на стол");
            idQuery = 0;
        }

        public void AddBot(byte ansv)
        {
            if (ansv == 0) RefillGameList();
            if (ansv == 1) MessageBox.Show("Такой игры уже не существует");
            if (ansv == 2) MessageBox.Show("В игре уже максимальное количество игроков");
            if (ansv == 255) MessageBox.Show("Ошибка в ответе от сервера на запрос: добавить бота");
            idQuery = 0;
        }

        public void ChangePass(byte ansv)
        {
            if (ansv == 0)
            {
                MessageBox.Show("Пароль успешно изменен");
                frm.ClientPass = newPass;
            }
            if (ansv == 1) MessageBox.Show("Старый пароль не верен");
            if (ansv == 2) MessageBox.Show("Старый или новый пароль пусты");
            if (ansv == 255) MessageBox.Show("Ошибка в ответе от сервера на запрос: сменить пароль");
            idQuery = 0;
        }

        #endregion *** ответы ********************************************************

        #endregion *** Коммуникации **************************************************

        #region *** чат *************************************************************

        void AddMessage(String UserName, String Message)
        {
            String UserClr = "";

            if (UserName == frm.ClientName)
            {
                UserClr = "<clr:000.050.150>";
                PlaySound(1);
            }
            else if (UserName != "Сервер")
                PlaySound(0);

            String s = UserClr + "[" + UserName + "] <clr:150.000.050>" + DateTime.Now.ToString() + " >> <clr:000.000.000>" + ReplaceSmile(Message) + "\r\n";
            txbChat.AddText(s);
            if (frm.bChatHist) AddChatHist(s);
        }

        String ReplaceSmile(String s)
        {
            for (int i = 0; i < SmL.Count; i++)
            {
                for (int j = 0; j < SmL[i].Length; j++)
                    s = s.Replace(SmL[i][j], "<" + i.ToString("000") + ">");
            }
            return s;
        }

        void AddChatHist(String s)
        {
            File.AppendAllText("History\\Chat.txt", s, Encoding.Default);
        }

        #endregion *** чат *************************************************************

        #region *** функции для панели предигрового меню ********************************

        void RefillGameTabs()
        {
            while (tabControl1.TabPages.Count > 1) tabControl1.TabPages.RemoveAt(tabControl1.TabPages.Count - 1);
            for (int i = 0; i < gmL.Count; i++)
            {
                if (gmL[i].GameIsPlay)
                {
                    tabControl1.TabPages.Add(gmL[i].Name);
                    tabControl1.TabPages[tabControl1.TabPages.Count - 1].Tag = gmL[i];
                }
            }
        }

        System.Drawing.Font fntListButton = new System.Drawing.Font("Arial", 8, FontStyle.Bold);

        void RefillGameList()
        {
            String actSelGame = lvGames.SelectedIndices.Count > 0 ? ((Game)(lvGames.SelectedItems[0].Tag)).Name : "";
            if (GameRequestCreate!="" ) actSelGame = GameRequestCreate;

            lvGames.Items.Clear();
            for (int i = 0; i < gmL.Count; i++)
            {
                String s = "";
                if (UserIsOnline(gmL[i].Moderator)) s = gmL[i].Name + " <<" + gmL[i].Moderator + ">>";
                else s = gmL[i].Name + " " + gmL[i].Moderator;

                ListViewItem itm = new ListViewItem(s);
                itm.Tag = gmL[i];
                itm.UseItemStyleForSubItems = false;

                // Добавиться/Удалиться (если игра не запущена)
                itm.SubItems.Add("");
                if (!gmL[i].GameIsPlay && gmL[i].Moderator != frm.ClientName)
                {
                    if (gmL[i].GetPlayarIndByName(frm.ClientName) == -1)  itm.SubItems[1].Text = "Добавиться"; 
                    else itm.SubItems[1].Text = "Удалиться";
                    itm.SubItems[1].Font = fntListButton;
                    itm.SubItems[1].BackColor = Color.Silver;
                }

                // Запуск игры (если она не запущена и юзер - модератор)
                itm.SubItems.Add("");
                if (gmL[i].Moderator == frm.ClientName && !gmL[i].GameIsPlay && gmL[i].pl.Count > 1)
                {
                    itm.SubItems[2].Text = "Запустить";
                    itm.SubItems[2].Font = fntListButton;
                    itm.SubItems[2].BackColor = Color.Silver;
                }

                // удаление игры (если модератор)
                itm.SubItems.Add("");
                if (gmL[i].Moderator == frm.ClientName)
                {
                    itm.SubItems[3].Text = "×";
                    itm.SubItems[3].Font = fntListButton;
                    itm.SubItems[3].BackColor = Color.Silver; 
                }

                lvGames.Items.Add(itm);
            }

            // выделение
            for (int i = 0; i < lvGames.Items.Count; i++)
            {
                if (actSelGame == ((Game)lvGames.Items[i].Tag).Name)
                {
                    lvGames.SelectedIndices.Add(i);
                    GameRequestCreate = "";
                    break;
                }
            }

            RefillUsersInGameList();

            RefillGameTabs();
        }

        void RefillUsersInGameList()
        {
            String actSelPl = lvPlayers.SelectedIndices.Count > 0 ? lvPlayers.SelectedItems[0].Text : "";

            lvPlayers.Items.Clear();
            Game game = lvGames.SelectedIndices.Count > 0 ? (Game)lvGames.SelectedItems[0].Tag : null;
            if (game == null) return;

            for (int i = 0; i < game.pl.Count; i++)
            {
                ListViewItem itm = new ListViewItem(game.pl[i].Name);
                itm.Tag = game.pl[i];
                itm.UseItemStyleForSubItems = false;

                // если игрок не в сети делаем его серым
                if (!UserIsOnline(game.pl[i].Name) && !game.pl[i].isBot)
                    itm.ForeColor = Color.Gray;

                // удаление игрока (если модератор)
                if (game.Moderator == frm.ClientName && game.Moderator != game.pl[i].Name) 
                {
                    itm.SubItems.Add("×");
                    itm.SubItems[1].Font = fntListButton;
                    itm.SubItems[1].BackColor = Color.Silver;
                }
                else { itm.SubItems.Add(""); }

                lvPlayers.Items.Add(itm);
            }

            // выделение
            for (int i = 0; i < lvPlayers.Items.Count; i++)
            {
                if (actSelPl == lvPlayers.Items[i].Text)
                {
                    lvPlayers.SelectedIndices.Add(i);
                    break;
                }
            }

        }

        void RefillBotList()
        {

            pnlBots.Controls.Clear();
            while (pnlBots.Controls.Count > 0)
            {
                Control cntrl = pnlBots.Controls[0];
                pnlBots.Controls.RemoveAt(0);
                cntrl.Dispose();
            }

            int y = 5;
            foreach (KeyValuePair<byte, string> bot in Gl.BotList)
            {
                Button btn = new Button();
                btn.Location = new Point(5, y);
                y += 25;
                btn.Size = new System.Drawing.Size(pnlBots.Width - 10, 20);
                btn.FlatStyle = FlatStyle.System;
                btn.Text = bot.Value;
                btn.Tag = bot.Key;
                btn.Click += new EventHandler(btnAddBot_Click);
                pnlBots.Controls.Add(btn);
            }
        }

        void EnterInGame(int idGame)
        {
            // заполняем массив положений камеры
            gmL[idGame].RefillPosPanaram(stepXPl, AngVis);
            EnterIdGame = idGame; // говорим, что после прихода обновления, необходимо войти в игру idGame
            // обновляем стол для этой игры
            frm.SendUpdateTable(gmL[idGame].Name);
        }

        #endregion *** функции для панели предигрового меню ********************************

        #region *** события для панели предигрового меню ********************************

        // кнопка отправки сообщения в чат (5)
        private void btnSendChat_Click(object sender, EventArgs e)
        {
            if (bWait) return;
            if (txbSendChat.Text == "") return;
            frm.SendUserMessage(txbSendChat.Text);
            idQuery = 5;
        }
        // кнопка запроса на начало игры модератором (25)
        private void btnBeginGame_Click(object sender, EventArgs e)
        {
            if (bWait) return;
            frm.SendBeginGame(SelGame.Name);
            idQuery = 25;
        }
        // кнопка запроса на удаление игры модератором (21)
        private void btnDelGame_Click(object sender, EventArgs e)
        {
            if (bWait) return;

            if (MessageBox.Show("Действительно удалить игру '" + SelGame.Name + "'?", "Удаление игры", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
            {
                frm.SendDeleteGame(SelGame.Name);
                idQuery = 21;
            }
        }
        // кнопка запроса на удаления юзера из игры модератором (22)
        private void btnDelUser_Click(object sender, EventArgs e)
        {
            if (bWait) return;
            if (MessageBox.Show("Действительно удалить игрока '" + SelPlr.Name + "'?", "Удаление игрока из игры", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
            {
                frm.SendDelUserInGame(SelGame.Name, SelPlr.Name);
                idQuery = 22;
            }
        }
        // кнопка запроса на заявку на участие в созданной игре (23)
        private void btnEnterIntoGame_Click(object sender, EventArgs e)
        {
            if (bWait) return;
            frm.SendAddIntoGame(SelGame.Name);
            idQuery = 23;
        }
        // кнопка запроса на создание новой игры (20)
        string GameRequestCreate = "";
        private void btnCreateGame_Click(object sender, EventArgs e)
        {
            if (bWait) return;

            FormAddGame frmAddGame = new FormAddGame();
            frmAddGame.ShowDialog(new FormAddGame.TryConfirmDelegate(TryConfirm_AddGame));
            frmAddGame.Dispose();
        }

        bool TryConfirm_AddGame(string val)
        {
            String GameName = val.Trim();
            if (GameName == "")
            {
                MessageBox.Show("Имя игры не может быть пустым");
                return false;
            }
            GameRequestCreate = GameName;
            frm.SendAddGame(GameName);
            idQuery = 20;

            return true;
        }

        // кнопка запроса на отмену заявки на участие в игре (24)
        private void btnCancelGame_Click(object sender, EventArgs e)
        {
            if (bWait) return;
            frm.SendCancelIntoGame(SelGame.Name);
            idQuery = 24;
        }
        // кнопка запроса на обновление листа игр (26)
        private void btnUpdateGameList_Click(object sender, EventArgs e)
        {
            btnUpdateGameList.Enabled = false;
            frm.SendUpdateGameList();
            idQuery = 26;
        }

        private void lvGames_SelectedIndexChanged(object sender, EventArgs e)
        {
            RefillUsersInGameList();
        }

        private void lbGames_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (SelGame != null)
            {
                // если игра запущена
                if (SelGame.GameIsPlay)
                {
                    for (int i = 0; i < SelGame.pl.Count; i++)
                        if (SelGame.pl[i].Name == frm.ClientName)
                        {
                            btnGame_Click(null, null);
                            break;
                        }
                }
            }
        }

        private void lvGames_MouseUp(object sender, MouseEventArgs e)
        {
            ListViewItem itm = lvGames.GetItemAt(e.X, e.Y);
            if (itm == null) return;
            Game game = (Game)itm.Tag;

            // список координаты x начала столбцов
            List<int> p = new List<int>() { 0 };
            for (int i = 0; i < lvGames.Columns.Count; i++) p.Add(p[p.Count - 1] + lvGames.Columns[i].Width);

            // добавиться/удалиться
            if (e.X >= p[1] && e.X < p[2])
            {
                // если не модератор
                if (game.Moderator != frm.ClientName)
                {
                    if (game.GetPlayarIndByName(frm.ClientName) == -1)
                    {
                        // добавиться в игру
                        btnEnterIntoGame_Click(null, null);
                    }
                    else
                    {
                        // удалиться из игры
                        btnCancelGame_Click(null, null);
                    }
                }
            }

            // запуск
            if (e.X >= p[2] && e.X < p[3])
            {
                // если Запускать можно
                if (game.Moderator == frm.ClientName && !game.GameIsPlay && game.pl.Count>1)
                {
                    // запустить игру
                    btnBeginGame_Click(null, null);
                }
            }

            // удаление
            if (e.X >= p[3] && e.X < p[4])
            {
                btnDelGame_Click(null, null);
            }
        }

        private void lvPlayers_MouseUp(object sender, MouseEventArgs e)
        {
            ListViewItem itm = lvPlayers.GetItemAt(e.X, e.Y);
            if (itm == null) return;
            Player plr = (Player)itm.Tag;

            // список координаты x начала столбцов
            List<int> p = new List<int>() { 0 };
            for (int i = 0; i < lvPlayers.Columns.Count; i++) p.Add(p[p.Count - 1] + lvPlayers.Columns[i].Width);

            // удаление
            if (e.X >= p[1] && e.X < p[2])
            {
                btnDelUser_Click(null, null);
            }
        }

        private void btnAddBot_Click(object sender, EventArgs e)
        {
            if (SelGame == null) { MessageBox.Show("Перед добавлением бота выберете игру в списке игр"); return; };

            // добавление бота
            if (bWait) return;
            frm.SendAddBot(SelGame.Name, (byte)((Button)sender).Tag);
            idQuery = 27;
        }

        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (bWait) return;
            // если возврат в меню
            if (tabControl1.SelectedIndex == 0)
            {
                btnShowMenu_Click(null, null);
                tBtnUpdateTable.Visible = false;
                tBtnShowLastCards.Visible = false;
                toolStripSeparator1.Visible = false;
            }
            else
            {
                EnterInGame(GetIndGameByName(((Game)tabControl1.SelectedTab.Tag).Name));
                tBtnUpdateTable.Visible = true;
                tBtnShowLastCards.Visible = true;
                toolStripSeparator1.Visible = true;
            }
        }

        #endregion *** события для панели предигрового меню ********************************

        private void button1_Click(object sender, EventArgs e)
        {
            InitializeGraphics();
        }

        int EnterIdGame = -1; // команда входа в игру сразу после обновления стола
        private void btnGame_Click(object sender, EventArgs e)
        {
            if (bWait) return;
            EnterInGame(GetIndGameByName(SelGame.Name));
        }

        private void btnShowMenu_Click(object sender, EventArgs e)
        {
            SetGameStatus(false);
        }

        private void BtnUpdTable_Click(object sender, EventArgs e)
        {
            if (actG == -1) return;
            frm.SendUpdateTable(gmL[actG].Name);
            idQuery = 51;
        }

        private void btnLastCard_Click(object sender, EventArgs e)
        {
            if (actG == -1) return;
            for (int i = 0; i < pl.Count; i++)
            {
                pl[i].cntTimeLastCard = 0;
            }
        }

        FormEdit frmPass = new FormEdit();
        String newPass = "";
        private void btnChangePass_Click(object sender, EventArgs e)
        {
            if (bWait) return;
            frmPass.el.Clear();
            frmPass.el.Add("Старый пароль", "");
            frmPass.el.Add("Новый пароль", "");
            frmPass.NameOk = "Изменить";
            frmPass.Text = "Смена пароля";
            frmPass.ShowDialog();
            if (frmPass.bOk)
            {
                String oldPass = frmPass.txb[0].Text.Trim();
                newPass = frmPass.txb[1].Text.Trim();
                if (oldPass == "" || newPass == "")
                {
                    MessageBox.Show("Пароль не может быть пустым");
                    return;
                }

                frm.SendChangePass(oldPass, newPass);
                idQuery = 6;
            }
        }

        public void Show(byte[] buf)
        {

        }

        private void FormMain_Shown(object sender, EventArgs e)
        {
            // устанавливаем макимальную длину сообщения в чат
            txbSendChat.MaxLength = (10240 - 1 - frm.ClientName.Length * 2 - 4 - 4) / 2 - 10;
        }

        private void btnClearChat_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Действительно очистить окно чата?", "Очищение окна чата", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
            {
                txbChat.Text = "";
            }
        }

        FormSettings frmSett = new FormSettings();
        private void btnSettings_Click(object sender, EventArgs e)
        {
            frmSett.chbxSound.Checked = frm.bSound;
            frmSett.chbxChatHist.Checked = frm.bChatHist;
            frmSett.ShowDialog();
            if (frmSett.bOk)
            {
                frm.bSound = frmSett.chbxSound.Checked;
                frm.bChatHist = frmSett.chbxChatHist.Checked;
            }
        }

        FormChatHist frmChatHist = new FormChatHist();
        private void btnShowChatHist_Click(object sender, EventArgs e)
        {
            if (File.Exists("History\\Chat.txt"))
                frmChatHist.txbChat.Text = File.ReadAllText("History\\Chat.txt", Encoding.Default);
            else
                frmChatHist.txbChat.Text = "";
            frmChatHist.ShowDialog();
        }

        private void btnShowSmiles_Click(object sender, EventArgs e)
        {
            frmSm.ShowDialog();
            int ind = frmSm.idSelSmile;
            if (frmSm.idSelSmile != -1)
            {
                if (ind < SmL.Count)
                {
                    if (SmL[ind].Length > 0)
                        txbSendChat.Text += SmL[ind][0];
                }
            }
        }

    }
}
